結束上一篇30天Flutter手滑系列 - 導航與路由(Navigation & Routing),我們透過範例,示範了在路由跳轉時傳遞參數,但我們都使用的是StatelessWidget去達成目的。如果這時候有個輸入表單,那就需要用StatefulWidget才能做到。
在我們進入進階的狀態管理前,先來認識跟狀態有關的這兩個Widgets吧。
StatelessWidget在App初始化之後就不能改變,它是immutable
。如果想要改變就得new一個新的StatelessWidget去做更換。
常見的StatelessWidget有:
延續之前的範例,我們來看一下其結構。
可以看到在整個Widget內都是使用靜態的Widget去做Navigation,過程中也不需要做任何狀態更新,應該是很好理解的。
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Home Page')),
body: Center(
child: RaisedButton(
child: Text('Register'),
onPressed: () {
Navigator.of(context).pushNamed('/register',
arguments: {'name': 'Raymond'}).then((value) {
// 新增第二個變數arguments
showDialog(
// 新增一個對話框,用來顯示回傳的值
context: context,
child: AlertDialog(
content: Text(value),
));
});
},
),
),
);
}
}
StatefulWidget可以在App內無限次的被重繪集及更新狀態,它是mutable
,需要重繪時可以調用setState()
,去標記自己為dirty狀態,為下次更新做準備。
常見的StatefulWidget有:
讓我們改寫範例,把傳遞參數部分透過使用者自行輸入名稱的方式去動態改變,因此我們需要改成Stateful Widget去操作這部分。
首先,定義一個StatefulWidget,然後把原本的MyHomePage
變成一個私有的State,並繼承自MyHomePage這個StatefulWidget。
然後在_MyHomePageState
中加入TextField
,而要獲取TextField的值有兩種方法,透過TextEditingController
,或是onChanged
回傳,在這我用前者。
定義了controller後就可以獲取TextField中的值,因此在原本Navigation的參數部分,就可以直接引用controller的值了。
class MyHomePage extends StatefulWidget { // 把MyHomePage設為一個StatefulWidget
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> { // 定義一個私有的Class,繼承自MyHomePage
final TextEditingController _controller = TextEditingController(); // 定義一個controler
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Home Page')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
width: 200.0,
margin: EdgeInsets.only(bottom: 10),
child: TextField(
controller: _controller, // 利用controller去獲取TextField的值
),
),
RaisedButton(
child: Text('Register'),
onPressed: () {
Navigator.of(context).pushNamed('/register',
arguments: {'name': _controller.text}).then((value) { // 參數接收來自TextField controller的值
// 新增第二個變數arguments
showDialog(
// 新增一個對話框,用來顯示回傳的值
context: context,
child: AlertDialog(
content: Text(value),
));
});
},
)
],
)),
);
}
}
範例展示:
- 盡可能用Stateless。
- Stateful Wigets盡量在越低層級的節點使用,避免過多不必要的重build。
setState((){})
會觸發State.build
,如果你的觸發是在根節點,會造成所有Widgets被重build,會是個效能問題。- 需要重build的Widget,盡量節點數越少越好。
結束了10天基礎的UI Widgets介紹,明天會開始介紹較為進階的狀態管理(State Management)。
https://flutter.dev/docs/development/ui/interactive
https://lizhaoxuan.github.io/2019/01/02/Flutter-%E4%BD%A0%E8%BF%98%E5%9C%A8%E6%BB%A5%E7%94%A8StatefulWidget%E5%90%97/
https://medium.com/flutter-community/flutter-stateful-vs-stateless-db325309deae
盡量在子節點使用 Stateful。有點不懂 XD
這邊我稍微改一下語句,基本上應該是跟第三點有關XD
盡量減少setState
重build的wigets數量是比較理想的,所以如果有狀態需要更新,盡量在越低層級的節點去做更新比較好。
如下圖,橘色框的重build的wigets會比紅色框內的少。